package com.softwareengineering.trafficapplicationv2;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;

/**
 * This file is part of the Traffic Simulation Application.
 * The Traffic Simulation Application is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * The Traffic Simulation Application is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with the Traffic Simulation Application.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * @file ActivityHighs.java
 * @author David Black, Chris Nelson, and Garrett Stibbs, (c) December 03, 2012
 * @description ActivityHighs handles global high score display and selection
 *              activity
 * @note Contains custom GetScoreAsyncTask for asynchronously fetching global
 *       high scores list from remote database
 *       Contains custom ScoreAdapter to handle populating
 * 		 of ListView containing all playable high score configurations
 * 
 */

public class ActivityHighs extends Activity implements Constants, OnItemClickListener {
	
	//	######################################
	//		ActivityHighs Member Variables
	//	######################################
	
	private ListView listView;	// handle to ListView to populate with high scores
	private ProgressBar progress;	// handle to ProgressBar to indicate asynchronous fetch in progress
	private GetScoreAsyncTask task;	// handle to GetScoreAsyncTask
	
	//	#########################
	//		GetScoreAsyncTask
	//	#########################

	//	handles fetching of global high scores asynchronously from remote database
	private class GetScoreAsyncTask extends
			AsyncTask<ArrayList<NameValuePair>, Void, String> {

		@Override	// task to do asynchronously
		protected String doInBackground(ArrayList<NameValuePair>... params) {
			String url = NetworkUtil.GET_URL;	// URL of remote script to returning high score configurations
			String response = "";	// initialize String to hold response
			HttpParams httpParams = new BasicHttpParams();	// create Object to hold Http parameters
			int timeoutConnection = 5000;	// specify connection timeout in milliseconds
			int timeoutSocket = 5000;	// specify socket timeout in milliseconds
			HttpConnectionParams.setConnectionTimeout(httpParams, timeoutConnection); // set connection timeout as specified
			HttpConnectionParams.setSoTimeout(httpParams, timeoutSocket);	// set socket timeout as specified
			DefaultHttpClient client = new DefaultHttpClient(httpParams);	// create HttpClient with desired Http parameters (timeouts)
			HttpPost httpPost = new HttpPost(url);	// use POST method to specified URL
			try {	// try
				HttpResponse execute = client.execute(httpPost);	// execute POST request from client
				InputStream content = execute.getEntity().getContent();	// create InputStream from content
				BufferedReader buffer = new BufferedReader(new InputStreamReader(content));	// create BufferedReader from InputStream
				String s = "";	// String to hold response line
				while ((s = buffer.readLine()) != null) {	// while line exists
					response += s;	// append line to response
				}
			} catch (Exception e) {	// fail
				e.printStackTrace();	// print stack trace for debugging
				return null;	// return null response
			}
			return response;	// return response on success
		}

		@Override	// task to do following execution
		protected void onPostExecute(String result) {
			if (result == null) {	// if execution failed
				didNotGetScores();	// call fail handler
				return;
			}

			try {
				Integer.parseInt(result);	// attempt to parse error
			}catch (NumberFormatException e) {
				didGetScores(result);	// execution success, call success handler with result
				return;
			}
			
			didNotGetScores();	// error parsed, call fail handler
		}
	}
	
	//	#####################
	//		ScoreAdapter
	//	#####################
	
	// handles population and display of high scores in ListView
	public class ScoreAdapter extends ArrayAdapter<Score> {
		// handle to ActivityHighs context
		private final Context context;
		
		// list of Score objects to hold high scores for each level
		private final ArrayList<Score> scores;

		//	#####################
		//		Constructor
		//	#####################
		
		public ScoreAdapter(Context context, ArrayList<Score> scores) {
			super(context, 0, scores);	// call super
			this.context = context;	// set handle to ActivityHighs context
			this.scores = scores;	// set scores
		}
		
		// returns Score for level at position
		public Score getScore(int position) {
			return scores.get(position); // return Score object
		}

		@Override	// returns View to display at position
		public View getView(int position, View convertView, ViewGroup parent) {
			RelativeLayout view;	// handle to container View
			TextView title;	// handle to level name View
			TextView high;	// handle to high score View
			
			if (convertView == null) {	// if convertView undefined
				view = new RelativeLayout(context);	// create container
				int padding = 20;	// specify container padding
				view.setPadding(padding, padding, padding, padding);	// set padding as specified
				view.setBackgroundDrawable(getResources().getDrawable(R.drawable.background_button_level));	// set container background
				
				title = new TextView(context, null, R.style.button);	// create level name TextView
				
				high = new TextView(context, null, R.style.button);	// create high score TextView
				RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);	// create new RelativeLayout parameters
				params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);	// align high score to container right
				high.setLayoutParams(params);	// set high score layout parameters
				
				view.addView(title);	// add level name TextView to container
				view.addView(high);		// add high score TextView to container
			} else {	// if convertView defined
				view = (RelativeLayout) convertView;	// get handle to container
				title = (TextView) view.getChildAt(0);	// get handle to level name View
				high = (TextView) view.getChildAt(1);	// get handle to high score View
			}

			Score score = scores.get(position);	// get Score object associated with position
			title.setText(score.getLevel_name());	// set text to "Level Name"
			high.setText("\u2605 " + score.getScore()); // set text to "★ X" where X=high score

			return view;	// return populated container
		}
	}
	
	//	####################################
	//		ActivityHighs Member Methods
	//	####################################

	// handler for successful fetching of high scores
	private void didGetScores(String result) {
		ArrayList<Score> scores = new ArrayList<Score>();	// create new list of Scores to populate from result
		try {	// try
			JSONArray rows = new JSONArray(result);	// attempt to create JSONArray from result
			for (int i = 0; i < rows.length(); i++) {	// for every row in JSONArray
				JSONObject row = rows.getJSONObject(i);	// get JSONObject at row
				Score score = new Score();	// create new default Score
				score.setLevel_name(row.getString("level_name"));	// set Score level name
				score.setLevel(row.getInt("level_id"));	// set Score level code corresponding to "Constants.java"
				score.setP_factor(row.getDouble("p_factor"));	// set Score politeness factor
				score.setScore(row.getInt("score"));	// set Score score
				score.setV0_limit(row.getDouble("v0_limit"));	// set Score speed limit
				if (scores.size() == 0)	// if no Scores in scores
					scores.add(score);	// add Score
				else if (scores.get(scores.size() - 1).getLevel() != score.getLevel()) // else if Score is unique (server response may have redundancies)
					scores.add(score);	// add Score
			}
		} catch (JSONException e) {	// JSONException
			e.printStackTrace();	// print stack trace for debugging
			didNotGetScores();		// call fail handler
			return;		// return
		}
		
		// successful population of Scores list
		progress.setVisibility(View.GONE);	// hide ProgressBar indicator
		listView.setAdapter(new ScoreAdapter(this, scores));	// set ListView adapter with populated scores
		listView.setOnItemClickListener(this);	// set onItemClickListener
	}

	// handler for failed fetching of high scores
	private void didNotGetScores() {
		task = new GetScoreAsyncTask();	// create new GetScoreAsyncTask
//		task.execute(null);	// attempt to fetch asynchronously again
	}
	
	// handles launching of appropriate game with Score attachment
	private void doLaunchGame(int scene, Score score) {
		Bundle bundle = new Bundle();	// create new bundle
		bundle.putBoolean("fromLoad", true);	// specify game will be loaded with custom configuration
		bundle.putInt("choice_Scen", scene);	// specify appropriate level
		bundle.putDouble("p_factor", score.getP_factor());	// set politeness factor
		bundle.putDouble("v0_limit", score.getV0_limit());	// set speed limit
		Intent intent = new Intent(this, ActivityGame.class);	// create Intent to launch ActivityGame activity
		intent.putExtra("config", bundle);	// put configuration bundle to Intent
		startActivity(intent);	// launch Intent
		finish();	// finish this activity to minimize stack use
	}

	//	#################
	//		Listeners
	//	#################

	@Override	// handles onCreate event
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);	// call super
		setContentView(R.layout.activity_highs);	// set appropriate layout

		listView = (ListView) findViewById(R.id.listview);	// get handle to ListView to populate with high scores
		progress = (ProgressBar) findViewById(R.id.progress);	// get handle to ProgressBar to indicate asynchronous task
		task = new GetScoreAsyncTask();	// create new GetScoreAsynTask to asynchronously fetch high score configurations from remote database
//		task.execute(null);	// start asynchronous task
	}
	
	@Override	// handles onItemClick, registered with ListView
	public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
		switch (position) {	// directed according to position corresponding to level codes in "Constants.java"
		case ON_RAMP:	// level code 0
		case LANE_CLOSURE:	// level code 1
		case UPHILL:	// level code 2
		case TRAFFIC_LIGHT:	// level code 4
		case FOUR_WAY: // level code 3
			Score score = ((ScoreAdapter)parent.getAdapter()).getScore(position);	// get Score associated with position
			doLaunchGame(position, score);	// launch game with appropriate level and Score attachment
			break;
		}
	}

	@Override	// handles onKeyDown events
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		switch (keyCode) {	// direct according to keyCode
		case KeyEvent.KEYCODE_BACK:	// Back button
			if (task != null)	// if on-going asynchronous task
				task.cancel(true);	// cancel asynchronous task
			startActivity(new Intent(this, ActivityLevels.class));	// launch ActivityLevels activity
			finish();	// finish this activity to minimize stack use
			return true;	// absorb event
		}
		return super.onKeyDown(keyCode, event);
	}

}
